﻿using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Xml;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using SharePointOfView.ExtensionMethods;

namespace SharePointOfView.EventReceivers
{
    /// <summary>
    /// Feature receiver that reads XML files and performs modifications to a web application's configuration file 
    /// </summary>
    public class WebConfigFeatureReceiver : SPFeatureReceiver
    {
        private const string uninstallInstallOnlyModificationsProperty = "UninstallInstallOnlyModifications";
        private const string installOnlyAttribute = "installonly";
        private const string installOnlyOwerPrefix = "INSTALLONLY_";

        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            // Get current web application
            SPWebApplication webApp = properties.Feature.Parent as SPWebApplication;

            if (webApp != null)
            {
                string owner = properties.Definition.Id.ToString();

                int modsCounter = 0;
                // Retrieve all ElementFiles
                List<XmlNode> configNodes = GetElementFileNodes(properties);
                    // For each elementfile defined in feature definition
                foreach (XmlNode configNode in configNodes)
                {
                    // Get all "add" nodes (nodes that will be added to web.config)
                    XmlNodeList addSectionsNodeList = configNode.SelectNodes("/actions/add");
                    if (addSectionsNodeList != null)
                    {
                        // For each add node, add section to web.config
                        foreach (XmlNode addSectionNode in addSectionsNodeList)
                        {
                            string configNodeXPath = addSectionNode.Attributes["path"].Value;
                            bool installOnly = false;
                            if (addSectionNode.Attributes[installOnlyAttribute] != null)
                            {
                                if (
                                    !bool.TryParse(addSectionNode.Attributes[installOnlyAttribute].Value,
                                                   out installOnly))
                                {
                                    installOnly = false;
                                }
                            }
                            // Add each subnode
                            foreach (XmlNode addedNode in addSectionNode.ChildNodes)
                            {
                                // Find node name
                                string configNodeName = addedNode.Name;
                                // Generate the unique node name by appending attributes (key/value)
                                foreach (XmlAttribute xAttr in addedNode.Attributes)
                                {
                                    configNodeName += string.Format("[@{0}='{1}']", xAttr.Name, xAttr.Value);
                                }
                                // Add config section
                                Utilities.WebConfigModification.AddConfigModification(webApp,
                                                                                      installOnly
                                                                                          ? installOnlyOwerPrefix +
                                                                                            owner
                                                                                          : owner, configNodeName,
                                                                                      configNodeXPath,
                                                                                      addedNode.OuterXml,
                                                                                      SPWebConfigModification.
                                                                                          SPWebConfigModificationType.
                                                                                          EnsureChildNode);
                                // And increment counter
                                modsCounter++;
                            }
                        }
                    }
                    // Get all "update" nodes (for attribute modification)
                    XmlNodeList updateSectionsNodeList = configNode.SelectNodes("/actions/update");
                    if (updateSectionsNodeList != null)
                    {
                        // For each update node, add section to web.config
                        foreach (XmlNode updateSectionNode in updateSectionsNodeList)
                        {
                            string configNodeXPath = updateSectionNode.Attributes["path"].Value;
                            // Add each subnode
                            foreach (XmlNode updatedNode in updateSectionNode.ChildNodes)
                            {
                                // Complete xpath
                                configNodeXPath += "/" + updatedNode.Name;
                                // Update each attribute
                                foreach (XmlAttribute xAttr in updatedNode.Attributes)
                                {
                                    Utilities.WebConfigModification.AddConfigModification(webApp, owner, xAttr.Name,
                                                                                          configNodeXPath,
                                                                                          xAttr.Value,
                                                                                          SPWebConfigModification.
                                                                                              SPWebConfigModificationType
                                                                                              .
                                                                                              EnsureAttribute);
                                }
                                // And increment counter
                                modsCounter++;
                            }
                        }
                    }
                }
                // Commit modifications if needed
                if (modsCounter > 0) Utilities.WebConfigModification.ApplyModifications(webApp);
            }
        }

        public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
        {
            SPWebApplication webApp = properties.Feature.Parent as SPWebApplication;
            string owner = properties.Definition.Id.ToString();
            // Remove sections
            Utilities.WebConfigModification.RemoveConfigModification(webApp, owner);
            // If force uninstall of add sections marked as install only, remove also these sections
            SPFeatureProperty forceUninstallProperty = properties.Feature.Properties[uninstallInstallOnlyModificationsProperty];
            if (forceUninstallProperty != null && forceUninstallProperty.Value.ToLower(CultureInfo.InvariantCulture) == "false")
            {
                Utilities.WebConfigModification.RemoveConfigModification(webApp, installOnlyOwerPrefix + owner);
            }
            // Commit modifications
            Utilities.WebConfigModification.ApplyModifications(webApp);
        }

        public override void FeatureInstalled(SPFeatureReceiverProperties properties)
        {
        }

        public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
        {
        }

        private List<XmlNode> GetElementFileNodes(SPFeatureReceiverProperties properties)
        {
            List<XmlNode> elementFileNodes = new List<XmlNode>();
            // Get the feature definition file as XML and find ElementFiles
            XmlNode featureNode = properties.Definition.GetXmlDefinition(CultureInfo.InvariantCulture);
            XmlNamespaceManager nsMgr = new XmlNamespaceManager(featureNode.OwnerDocument.NameTable);
            nsMgr.AddNamespace("base", featureNode.NamespaceURI);
            XmlNodeList elementFiles = featureNode.SelectNodes("base:ElementManifests/base:ElementFile", nsMgr);
            if (elementFiles != null)
            {
                // For each elementfile defined in feature definition
                foreach (XmlNode elementFile in elementFiles)
                {
                    // Get the location (relative path from feature folder
                    string relativeFilename = elementFile.Attributes["Location"].Value;
                    // Get real file path
                    string fileName = Path.Combine(properties.Definition.RootDirectory, relativeFilename);
                    // Initialize and load the file (must be XML of course !
                    XmlDocument doc = new XmlDocument();
                    doc.Load(fileName);
                    // Add XmlDocument to the list
                    elementFileNodes.Add(doc);
                }
            }

            return elementFileNodes;
        }
    }
}